﻿package rob.entity{
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.geom.Matrix;
	import rob.data.Vert3d;
	import rob.core.Camera3d;
	import rob.maths.VertexMaths;
	import rob.util.DisplayUtil;
	
	public class Structure{
		// top/basevert are 2 3d points
		// topPoint is used because top/base lie in gfx mc (different coord system)
		// gfx is the holder clip
		// base and top are clips we connect the points in them
		// sides holds array of Side objects
		// basePnts and topPnts are point mcs in base and top
		// scales are base/top scale
		// baseHitTest is floor object representing base of building
		private var _visible:Boolean;
		private var _topVert:Vert3d, _baseVert:Vert3d, _topPoint:Point;
		private var _gfx:Sprite, _height:Number;
		private var _base:Sprite, _top:Sprite, _sides:Array, _visSides:Array;
		private var _basePnts:Array, _topPnts:Array;
		private var _scales:Array;
		private var _baseHitTest:Floor;
		
		public function Structure(s:Sprite):void{
			// define two points (one for base & one for top)
			// fixed height atm
			_height = 300 + 200*Math.random();
			_topVert = new Vert3d(0,_height,0);
			_baseVert = new Vert3d(0,0,0);
			// set gfx as sprite
			_gfx = s;
			// work out position (TODO: general transform)
			_topVert.x += s.x; 	_topVert.z += s.y;
			_baseVert.x += s.x; _baseVert.z += s.y;
			_scales = [1,1];
			
			// get refs to inner top/base mcs
			_base = _gfx.base;
			_top = _gfx.top;			
			
			// pick up point object, ref & set invisible
			_basePnts = _extractPoints(_base);
			_topPnts = _extractPoints(_top);
			
			// use _basePnts to
			// draw another base for collision / offscreen detection
			// this graphic will be attached to the floor by Root.as
			// NOTE: we only want outline of base
				var bht:Sprite = new Sprite();
				with(bht.graphics){
					lineStyle(8,0);	beginFill(0,1);
					moveTo(_basePnts[0].x,_basePnts[0].y);
					for(var ic:uint in _basePnts) lineTo(_basePnts[ic].x,_basePnts[ic].y);
					endFill();
				}
			
				// must define position here
				bht.x = _gfx.x;	bht.y = _gfx.y;
				_baseHitTest = new Floor(bht);
				// transform
				var mat:Matrix = _gfx.transform.matrix;
				_baseHitTest.transform.matrix = mat;
				_baseHitTest.visible = false;
			
			// get hold of scene_mc to find real position of a base point
			// this is needed for side visiblity detection
			var gp:Sprite = _base.parent.parent;
			var pnt2d:Point, pnt3d:Vert3d;

			// construct sides
			_sides = [];
			var bps:Array = _basePnts, tps:Array = _topPnts;
			var L:uint = _basePnts.length, j:uint, sd:Side;
			// check deteminant of matrix: need to swap normals if negative
			var det:Number = mat.a * mat.d - mat.b * mat.c;
			var swapNormalDir:Boolean = (det < 0);
			// loop thru pnts and constuct sides
			for(var i:uint = 0; i < L; i++){
				j = (i+1)%L;
					// project one base point to use as a real 3d position
					pnt2d = new Point(0.5*(bps[i].x + bps[j].x),0.5*(bps[i].y+bps[j].y));
					pnt2d = _base.localToGlobal(pnt2d);
					pnt2d = gp.globalToLocal(pnt2d);
					pnt3d = new Vert3d(pnt2d.x,0,pnt2d.y);
				sd = new Side([bps[i],tps[i],tps[j],bps[j]],[_base,_top],pnt3d,swapNormalDir);
				_gfx.addChild(sd);
				_sides.push(sd);
			}
			
			// make sure top is on top
			_gfx.addChild(_top);
			// set invisible
			_visible = true;
			setVisible(false);
			_visSides = [];
		}

		public function update(c:Camera3d,sc:Sprite):void{
			VertexMaths.project(_topVert,c);
			VertexMaths.project(_baseVert,c);
			// position gfx instead of base
			_gfx.x = _baseVert.sx;
			_gfx.y = _baseVert.sy;
			// have to transform top point since lies inside _gfx
			_topPoint = new Point(_topVert.sx,_topVert.sy);
			_topPoint = sc.localToGlobal(_topPoint);
			_topPoint = _gfx.globalToLocal(_topPoint);
			_top.x = _topPoint.x;	_top.y = _topPoint.y;
		}

		public function setVisible(b:Boolean):void{
			var sd:Side;
			if(!b && _visible){
				for each(sd in _sides) sd.visible = false;
				_top.visible = _base.visible = false;
				_visible = false;
			}
			else if(b && !_visible){
				for each(sd in _sides) sd.visible = true;
				_top.visible = _base.visible = true;
				_visible = true;
			}
		}
		
		// expects degrees input
		public function rotateBy(deg:Number):void{
			_gfx.rotation += deg;
		}
		
		public function setDimensions(c:Camera3d):void{
			_top.scaleX = _top.scaleY = c.sz / (c.position.y - _height);
			_gfx.scaleX * _top.scaleX;
			_gfx.scaleY * _top.scaleY;
			_base.scaleX = _base.scaleY = c.sz / c.position.y;
			_scales = [_base.scaleX,_top.scaleX];
		}
		
		// extract mcs named p_0, p_1, etc.
		// also set these helper mcs invisible
		private function _extractPoints(s:Sprite):Array{
			var c:Object, bits:Array, a:Array;
			a = [];
			for(var i:uint = 0; i < s.numChildren; i++){
				c = s.getChildAt(i);
				if(c is pnt){
					// should have name: p_i for integer i, extract i
					bits = c.name.split("_");
					a[Number(bits[1])] = c;
					c.visible = false;
				}
			}
			return a;
		}
		
		private function _setPointsInvisible(s:Sprite):Array{
			for (var i:uint = 0; i < s.numChildren; i++){
				c = s.getChildAt(i);
				if(c is pnt) c.visible = false;
			}
		}
		
		public function get order():Number{
			var d:Number = -(Math.abs(_gfx.x) + Math.abs(_gfx.y))
			return d;
		}
		
		public function get baseHitTest():Floor{return _baseHitTest;}
		public function get visSides():Array{return _visSides;}
		public function get visible():Boolean{ return _visible; }
		public function get gfx():Sprite{ return _gfx; }
		public function get base():Sprite{ return _base; }
		//public function get top():Sprite{ return _top; }
		public function get sides():Array{ return _sides; }
		
		public function set visSides(vs:Array):void{_visSides = vs;}
	}
}